package com.wfs.aqs;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author wfs
 * @date 2022-02-24 16:32
 */
public class MiniArrayBrokingQueue<T> implements BrokingQueue {

    //线程并发控制
    private Lock lock=new ReentrantLock();

    /**
     * 当生产者线程生产数据时，他会先检查当前queues是否已经满了，如果已经满了，需要经生产者线程 调用 notFull.await()
     * 进入到notFull条件队列挂起。等待消费者线程消费数据是唤醒
     */
    private Condition notFull=lock.newCondition();

    /**
     * 当消费者线程消费数据时，它先检查当前queues中是否有数据，如果没有数据，需要将当前消费者线程 调用notEmpty.await()
     * 进入到notEmpty条件队列挂起。等待生产者线程生产数据时唤醒
     */
    private Condition notEmpty=lock.newCondition();
    //底层储存元素数据
    private Object[] queues;
    //数据长度
    private int size;
    /**
     * count 当前队列中可以被消费的数据量
     * putptr 记录生产者存放数据的下一次位置。每个生产者生产完数据后回见putptr++
     * taskptr 记录消费者消费数据的下一次的位置。每个消费消费完一个数据后，会将taskptr ++
     */
    private int count,putptr,takeptr;

    public MiniArrayBrokingQueue(int size) {
        this.size = size;
        this.queues = new Object[size];
    }

    @Override
    public void put(Object element) {
        lock.lock();
        try {
            //首先判断队列是否已经满了
            if (count == this.size){
                notFull.await();
            }
            //执行到这里 说明列未满 可以向队列中存放数据了
            this.queues[putptr]=element;
            putptr++;
            if (putptr==size){
                putptr=0;
            }
            count++;
            //向队列中成功放入一个元素后，需要给notEmpty 一个唤醒的信号
            notEmpty.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    @Override
    public Object take() {
        lock.lock();
        try {
            //判断数据是是否可以被消费
            if (count == 0){
                notEmpty.await();
            }
            //执行到此处说明有数据可以消费
            Object element=this.queues[takeptr];
            takeptr++;
            if (takeptr==size){
                takeptr=0;
            }
            count--;
            //队列成功消费了一个元素后，需要给notFull一个唤醒信号
            notFull.signal();
            return element;
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {

        }
        return null;
    }

    public static void main(String[] args) {
        BrokingQueue<Integer> brokingQueue=new MiniArrayBrokingQueue(2);
        Thread producer = new Thread(() -> {
            int i = 0;
            while(true) {
                i ++;
                if(i == 10) i = 0;

                try {
                    System.out.println("生产数据：" + i);
                    brokingQueue.put(Integer.valueOf(i));
                    TimeUnit.MILLISECONDS.sleep(200);
                } catch (InterruptedException e) {
                }
            }
        });
        producer.start();
        Thread consumer = new Thread(() -> {
            while(true) {
                try {
                    Integer result = brokingQueue.take();
                    System.out.println("消费者消费：" + result);
                    TimeUnit.MILLISECONDS.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        consumer.start();


    }
}
